package concurrency.chapter9;

import java.util.stream.Stream;

//25讲-多Produce多Consume之间的通讯导致出现程序假死的原因分析[超清版]

/**
 * 永远在while循环里而不是if语句下使用wait。这样，循环会在线程睡眠前后都检查wait的条件，并在条件实际上并未改变的情况下处理唤醒通知。
 * 多线程消费或者多线程生产时候，当调到notify（）方法的时候，可能只叫醒同类的线性，说明白一点就是；生产者叫醒了另外一个生产者然后进入到wait中；解决办法把notify（）方法改成 notifyAll()
 * <p>
 * 不是有synchronized关键字加锁了吗？
 * 一个线程执行了wait方法以后，它不会再继续执行了，直到被notify唤醒。
 * 那么唤醒以后从何处开始执行？这是解决这里出错原因的关键。
 * 结论：就是用if判断的话，唤醒后线程会从wait之后的代码开始运行，但是不会重新判断if条件，直接继续运行if代码块之后的代码，
 * 而如果使用while的话，也会从wait之后的代码运行，但是唤醒后会重新判断循环条件，如果不成立再执行while代码块之后的代码块，成立的话继续wait。
 */
public class ProduceConsumerVersion3 {
    private int i = 0;
    final private Object LOCK = new Object();
    private volatile boolean isProduced = false;

    private void produce() {
        synchronized (LOCK) {
            //不能用if，为什么？？？
            //已经处在if方法体内的线程被唤醒时，它不会再去执行if条件判断，而是顺序往下执行
            //if (isProduced) {
            //唤醒后会对while条件再次做判断
            while (isProduced) {
                try {
                    //即wait(0)，一定要有个monitor
                    //wait时会释放锁，直到被notify唤醒为runnable状态，再抢锁
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            i++;
            System.out.println("P->" + i);
            LOCK.notifyAll();
            isProduced = true;

        }
    }

    private void consume() {
        synchronized (LOCK) {
            //if (!isProduced) {
            while (!isProduced) {
                try {
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("C->" + i);
            LOCK.notifyAll();
            isProduced = false;
        }
    }

    public static void main(String[] args) {
        ProduceConsumerVersion3 pc = new ProduceConsumerVersion3();
        Stream.of("P1", "P2").forEach(n -> new Thread() {
            @Override
            public void run() {
                while (true)
                    pc.produce();
            }
        }.start());
        Stream.of("C1", "C2").forEach(n -> new Thread() {
            @Override
            public void run() {
                while (true)
                    pc.consume();
            }
        }.start());
    }

}
//生产多次、消费多次
//P->18430
//P->18431
//P->18432
//P->18433
//C->18433
//C->18433
//C->18433
//C->18433